#include "hash.h"
#include "pkt_queue.h"
#include "l2.h"
#include "prot_ip.h"
#include "prot_arp.h"
#include "prot_ipv6.h"

#define L2_ENTRY_NUM  65536

struct l2entry {
  uint16_t ethertype;
  l2_func_t rx_func;
  l2_func_t tx_func;
};

static bool l2_default_rx(struct llbuf *ll)
{
  return false;
}

static bool l2_default_tx(struct llbuf *ll)
{
  return false;
}

struct l2entry l2config[] = {
  {.ethertype = 0, .rx_func=l2_default_rx, .tx_func = l2_default_tx},
  {.ethertype = ETHERTYPE_IP, .rx_func=ip_rx, .tx_func = ip_tx},
  {.ethertype = ETHERTYPE_ARP, .rx_func=arp_rx, .tx_func = arp_tx},
  {.ethertype = ETHERTYPE_IPV6, .rx_func=ipv6_rx, .tx_func = ipv6_tx},
};

static uint16_t l2addr[L2_ENTRY_NUM];

static int debug_l2 = 0;


#define L2_LOG(fmt, ...)      \
  do {                        \
    if (debug_l2)             \
      print_log("[%s-%d]"fmt, __func__, __LINE__, ##__VA_ARGS__);\
  }while(0)


bool l2_rx(struct llbuf *ll)
{
  struct ethhdr *ethhdr = (struct ethhdr *)ll->l2_ptr;
  memcpy (ll->dst_mac, ethhdr->h_dest,  ETH_ALEN);
  memcpy (ll->src_mac, ethhdr->h_source, ETH_ALEN);

  ll->frame_type = ntohs(ethhdr->h_proto);
  ll->frame_size = ll->size;
  ll->l3_ptr = ll->l2_ptr + ETH_HLEN;
  ll->l3_size = ll->l2_size - ETH_HLEN;
  uint16_t index = l2addr[ll->frame_type];
  return l2config[index].rx_func(ll);
}

bool l2_tx(struct llbuf *ll)
{
  uint16_t index = l2addr[ll->frame_type];
  return l2config[index].tx_func(ll);
}

BOLT(debug_l2, "debug l2 on")
{
  debug_l2 = 1;
}

BOLT(undebug_l2, "debug l2 off")
{
  debug_l2 = 0;
}

BOLT_PROFILE(l2, "", l2_profile, desc)
{
  if (debug_l2)
    cmd_load_profile(desc, "debug l2 on\n");
}


bool l2_init(void)
{
  memset(&l2addr[0], 0, sizeof(l2addr));
  unsigned int i;
  for (i = 0; i < sizeof(l2config)/sizeof(l2config[0]);i++) {
    l2addr[l2config[i].ethertype] = (uint16_t)i;
  }
  return true;
}

void l2_deinit(void)
{
}
